home *** CD-ROM | disk | FTP | other *** search
/ PD Collection CD 1 / PD Collection CD 1.iso / textual / pdftops / xpdf / c++ / XOutputDev < prev    next >
Text File  |  1996-05-23  |  39KB  |  1,498 lines

  1. //========================================================================
  2. //
  3. // XOutputDev.cc
  4. //
  5. // Copyright 1996 Derek B. Noonburg
  6. //
  7. //========================================================================
  8.  
  9. #ifdef __GNUC__
  10. #pragma implementation
  11. #endif
  12.  
  13. #include <stdio.h>
  14. #include <stddef.h>
  15. #include <string.h>
  16. #include <math.h>
  17. #include <gmem.h>
  18. #include <GString.h>
  19. #include <LTKWindow.h>
  20. #include <LTKScrollingCanvas.h>
  21. #include "Object.h"
  22. #include "Stream.h"
  23. #include "GfxState.h"
  24. #include "GfxFont.h"
  25. #include "Error.h"
  26. #include "Flags.h"
  27. #include "XOutputDev.h"
  28.  
  29. #include "XOutputFontInfo.h"
  30.  
  31. //------------------------------------------------------------------------
  32. // Constants and macros
  33. //------------------------------------------------------------------------
  34.  
  35. #define numTmpSubpaths 16    // number of elements in temporary arrays
  36.  
  37. #define xoutRound(x) ((int)(x + 0.5))
  38.  
  39. //------------------------------------------------------------------------
  40. // Misc types
  41. //------------------------------------------------------------------------
  42.  
  43. struct BoundingRect {
  44.   short xMin, xMax;        // min/max x values
  45.   short yMin, yMax;        // min/max y values
  46. };
  47.  
  48. struct RGBColor {
  49.   int r, g, b;
  50. };
  51.  
  52. //------------------------------------------------------------------------
  53. // Command line options
  54. //------------------------------------------------------------------------
  55.  
  56. int rgbCubeSize = defaultRGBCube;
  57.  
  58. //------------------------------------------------------------------------
  59. // Font map
  60. //------------------------------------------------------------------------
  61.  
  62. struct FontMapEntry {
  63.   char *pdfFont;
  64.   char *xFont;
  65.   char **encoding;
  66.   int encodingSize;
  67. };
  68.  
  69. static FontMapEntry fontMap[] = {
  70.   {"Courier",               "-*-courier-medium-r-*-*-%s-*-*-*-*-*-iso8859-1",
  71.    isoEncoding,          isoEncodingSize},
  72.   {"Courier-Bold",          "-*-courier-bold-r-*-*-%s-*-*-*-*-*-iso8859-1",
  73.    isoEncoding,          isoEncodingSize},
  74.   {"Courier-BoldOblique",   "-*-courier-bold-o-*-*-%s-*-*-*-*-*-iso8859-1",
  75.    isoEncoding,          isoEncodingSize},
  76.   {"Courier-Oblique",       "-*-courier-medium-o-*-*-%s-*-*-*-*-*-iso8859-1",
  77.    isoEncoding,          isoEncodingSize},
  78.   {"Helvetica",             "-*-helvetica-medium-r-*-*-%s-*-*-*-*-*-iso8859-1",
  79.    isoEncoding,          isoEncodingSize},
  80.   {"Helvetica-Bold",        "-*-helvetica-bold-r-*-*-%s-*-*-*-*-*-iso8859-1",
  81.    isoEncoding,          isoEncodingSize},
  82.   {"Helvetica-BoldOblique", "-*-helvetica-bold-o-*-*-%s-*-*-*-*-*-iso8859-1",
  83.    isoEncoding,          isoEncodingSize},
  84.   {"Helvetica-Oblique",     "-*-helvetica-medium-o-*-*-%s-*-*-*-*-*-iso8859-1",
  85.    isoEncoding,          isoEncodingSize},
  86.   {"Symbol",                "-*-symbol-medium-r-*-*-%s-*-*-*-*-*-adobe-fontspecific",
  87.    symbolEncoding,       symbolEncodingSize},
  88.   {"Times-Bold",            "-*-times-bold-r-*-*-%s-*-*-*-*-*-iso8859-1",
  89.    isoEncoding,          isoEncodingSize},
  90.   {"Times-BoldItalic",      "-*-times-bold-i-*-*-%s-*-*-*-*-*-iso8859-1",
  91.    isoEncoding,          isoEncodingSize},
  92.   {"Times-Italic",          "-*-times-medium-i-*-*-%s-*-*-*-*-*-iso8859-1",
  93.    isoEncoding,          isoEncodingSize},
  94.   {"Times-Roman",           "-*-times-medium-r-*-*-%s-*-*-*-*-*-iso8859-1",
  95.    isoEncoding,          isoEncodingSize},
  96.   {"ZapfDingbats",          "-*-zapfdingbats-medium-r-*-*-%s-*-*-*-*-*-itc-fontspecific",
  97.    zapfDingbatsEncoding, zapfDingbatsEncodingSize},
  98.   {NULL}
  99. };
  100.  
  101. //------------------------------------------------------------------------
  102. // Font substitutions
  103. //------------------------------------------------------------------------
  104.  
  105. struct FontSubst {
  106.   char *xFont;
  107.   double mWidth;
  108. };
  109.  
  110. // index: {symbolic:12, fixed:8, serif:4, sans-serif:0} + bold*2 + italic
  111. static FontSubst fontSubst[16] = {
  112.   {"-*-helvetica-medium-r-*-*-%s-*-*-*-*-*-iso8859-1",       0.833},
  113.   {"-*-helvetica-medium-o-*-*-%s-*-*-*-*-*-iso8859-1",       0.833},
  114.   {"-*-helvetica-bold-r-*-*-%s-*-*-*-*-*-iso8859-1",         0.889},
  115.   {"-*-helvetica-bold-o-*-*-%s-*-*-*-*-*-iso8859-1",         0.889},
  116.   {"-*-times-medium-r-*-*-%s-*-*-*-*-*-iso8859-1",           0.788},
  117.   {"-*-times-medium-i-*-*-%s-*-*-*-*-*-iso8859-1",           0.722},
  118.   {"-*-times-bold-r-*-*-%s-*-*-*-*-*-iso8859-1",             0.833},
  119.   {"-*-times-bold-i-*-*-%s-*-*-*-*-*-iso8859-1",             0.778},
  120.   {"-*-courier-medium-r-*-*-%s-*-*-*-*-*-iso8859-1",         0.600},
  121.   {"-*-courier-medium-o-*-*-%s-*-*-*-*-*-iso8859-1",         0.600},
  122.   {"-*-courier-bold-r-*-*-%s-*-*-*-*-*-iso8859-1",           0.600},
  123.   {"-*-courier-bold-o-*-*-%s-*-*-*-*-*-iso8859-1",           0.600},
  124.   {"-*-symbol-medium-r-*-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576},
  125.   {"-*-symbol-medium-r-*-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576},
  126.   {"-*-symbol-medium-r-*-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576},
  127.   {"-*-symbol-medium-r-*-*-%s-*-*-*-*-*-adobe-fontspecific", 0.576}
  128. };
  129.  
  130. //------------------------------------------------------------------------
  131. // Constructed characters
  132. //------------------------------------------------------------------------
  133.  
  134. #define lastRegularChar 0x0ff
  135. #define firstSubstChar  0x100
  136. #define lastSubstChar   0x104
  137. #define firstConstrChar 0x105
  138. #define lastConstrChar  0x106
  139. #define firstMultiChar  0x107
  140. #define lastMultiChar   0x10d
  141.  
  142. // substituted chars
  143. static Guchar substChars[] = {
  144.   0x27,                // 100: quotesingle --> quoteright
  145.   0x2d,                // 101: emdash --> hyphen
  146.   0x2d,                // 102: minus --> hyphen
  147.   0x2f,                // 103: fraction --> slash
  148.   0xb0,                // 104: ring --> degree
  149. };
  150.  
  151. // constructed chars
  152. // 105: bullet
  153. // 106: trademark
  154.  
  155. // built-up chars
  156. static char *multiChars[] = {
  157.   "fi",                // 107: fi
  158.   "fl",                // 108: fl
  159.   "OE",                // 109: OE
  160.   "oe",                // 10a: oe
  161.   "...",            // 10b: ellipsis
  162.   "``",                // 10c: quotedblleft
  163.   "''"                // 10d: quotedblright
  164. };
  165.  
  166. // ignored chars
  167. // 10c: Lslash
  168. // 10d: Scaron
  169. // 10e: Zcaron
  170. // 10f: Ydieresis
  171. // 110: breve
  172. // 111: caron
  173. // 112: circumflex
  174. // 113: dagger
  175. // 114: daggerdbl
  176. // 115: dotaccent
  177. // 116: dotlessi
  178. // 117: florin
  179. // 118: grave
  180. // 119: guilsinglleft
  181. // 11a: guilsinglright
  182. // 11b: hungarumlaut
  183. // 11c: lslash
  184. // 11d: ogonek
  185. // 11e: perthousand
  186. // 11f: quotedblbase
  187. // 120: quotesinglbase
  188. // 121: scaron
  189. // 122: tilde
  190. // 123: zcaron
  191.  
  192. //------------------------------------------------------------------------
  193. // XOutputFont
  194. //------------------------------------------------------------------------
  195.  
  196. // Note: if real font is substantially narrower than substituted
  197. // font, the size is reduced accordingly.
  198. XOutputFont::XOutputFont(GfxFont *gfxFont, double m11, double m12,
  199.              double m21, double m22, Display *display1) {
  200.   GString *pdfFont;
  201.   FontMapEntry *p;
  202.   char **encoding;
  203.   int encodingSize;
  204.   char *fontNameFmt;
  205.   char fontName[200], fontSize[100];
  206.   GBool rotated;
  207.   double size;
  208.   int startSize, sz;
  209.   int index;
  210.   int code, code2;
  211.   double w1, w2;
  212.   char *charName;
  213.  
  214.   // init
  215.   tag = gfxFont->getTag()->copy();
  216.   mat11 = m11;
  217.   mat12 = m12;
  218.   mat21 = m21;
  219.   mat22 = m22;
  220.   display = display1;
  221.   xFont = NULL;
  222.   revMap = NULL;
  223.  
  224.   // construct X font name
  225.   pdfFont = gfxFont->getName();
  226.   if (pdfFont) {
  227.     for (p = fontMap; p->pdfFont; ++p) {
  228.       if (!pdfFont->cmp(p->pdfFont))
  229.     break;
  230.     }
  231.   } else {
  232.     p = NULL;
  233.   }
  234.   if (p && p->pdfFont) {
  235.     fontNameFmt = p->xFont;
  236.     encoding = p->encoding;
  237.     encodingSize = p->encodingSize;
  238.   } else {
  239.     encoding = isoEncoding;
  240.     encodingSize = isoEncodingSize;
  241. //~ Some non-symbolic fonts are tagged as symbolic.
  242. //~    if (gfxFont->isSymbolic()) {
  243. //~      index = 12;
  244. //~      encoding = symbolEncoding;
  245. //~      encodingSize = symbolEncodingSize;
  246. //~    } else
  247.     if (gfxFont->isFixedWidth()) {
  248.       index = 8;
  249.     } else if (gfxFont->isSerif()) {
  250.       index = 4;
  251.     } else {
  252.       index = 0;
  253.     }
  254.     if (gfxFont->isBold())
  255.       index += 2;
  256.     if (gfxFont->isItalic())
  257.       index += 1;
  258.     if (!gfxFont->isSymbolic()) {
  259.       w1 = gfxFont->getWidth('m');
  260.       w2 = fontSubst[index].mWidth;
  261.       if (w1 > 0.01 && w1 < 0.9 * w2) {
  262.     w1 /= 0.9 * w2;
  263.     mat11 *= w1;
  264.     mat12 *= w1;
  265.     mat21 *= w1;
  266.     mat22 *= w1;
  267.       }
  268.     }
  269.     fontNameFmt = fontSubst[index].xFont;
  270.   }
  271.  
  272.   // compute size, normalize matrix
  273.   size = sqrt(mat21*mat21 + mat22*mat22);
  274.   mat11 = mat11 / size;
  275.   mat12 = -mat12 / size;
  276.   mat21 = mat21 / size;
  277.   mat22 = -mat22 / size;
  278.   startSize = (int)size;
  279.  
  280.   // try to get a rotated font?
  281.   rotated = !(mat11 > 0 && mat22 > 0 && fabs(mat11/mat22 - 1) < 0.2 &&
  282.           fabs(mat12) < 0.01 && fabs(mat21) < 0.01);
  283.  
  284.   // open X font -- if font is not found (which means the server can't
  285.   // scale fonts), try progressively smaller and then larger sizes
  286.   //~ This does a linear search -- it should get a list of fonts from
  287.   //~ the server and pick the closest.
  288.   if (rotated)
  289.     sprintf(fontSize, "[%s%0.2f %s%0.2f %s%0.2f %s%0.2f]",
  290.         mat11<0 ? "~" : "", fabs(mat11 * startSize),
  291.         mat12<0 ? "~" : "", fabs(mat12 * startSize),
  292.         mat21<0 ? "~" : "", fabs(mat21 * startSize),
  293.         mat22<0 ? "~" : "", fabs(mat22 * startSize));
  294.   else
  295.     sprintf(fontSize, "%d", startSize);
  296.   sprintf(fontName, fontNameFmt, fontSize);
  297.   xFont = XLoadQueryFont(display, fontName);
  298.   if (!xFont) {
  299.     for (sz = startSize; sz >= 1; --sz) {
  300.       sprintf(fontSize, "%d", sz);
  301.       sprintf(fontName, fontNameFmt, fontSize);
  302.       if ((xFont = XLoadQueryFont(display, fontName)))
  303.     break;
  304.     }
  305.     if (!xFont) {
  306.       for (sz = startSize + 1; sz < 10; ++sz) {
  307.     sprintf(fontSize, "%d", sz);
  308.     sprintf(fontName, fontNameFmt, fontSize);
  309.     if ((xFont = XLoadQueryFont(display, fontName)))
  310.       break;
  311.       }
  312.       if (!xFont) {
  313.     sprintf(fontSize, "%d", startSize);
  314.     sprintf(fontName, fontNameFmt, fontSize);
  315.     error(0, "Failed to open font: '%s'", fontName);
  316.     return;
  317.       }
  318.     }
  319.   }
  320.  
  321.   // construct forward and reverse map
  322.   revMap = (Guchar *)gmalloc(encodingSize * sizeof(Guchar));
  323.   for (code = 0; code < 256; ++code) {
  324.     charName = gfxFont->getCharName(code);
  325.     if (charName) {
  326.       code2 = gfxFont->lookupCharName(charName, encoding, encodingSize, code);
  327.       if (code2 >= 0) {
  328.     map[code] = (Gushort)code2;
  329.     revMap[code2] = (Guchar)code;
  330.       }
  331.     }
  332.   }
  333. }
  334.  
  335. XOutputFont::~XOutputFont() {
  336.   delete tag;
  337.   if (xFont)
  338.     XFreeFont(display, xFont);
  339.   if (revMap)
  340.     gfree(revMap);
  341. }
  342.  
  343. //------------------------------------------------------------------------
  344. // XOutputFontCache
  345. //------------------------------------------------------------------------
  346.  
  347. XOutputFontCache::XOutputFontCache(Display *display1) {
  348.   int i;
  349.  
  350.   display = display1;
  351.   for (i = 0; i < fontCacheSize; ++i)
  352.     fonts[i] = NULL;
  353.   numFonts = 0;
  354. }
  355.  
  356. XOutputFontCache::~XOutputFontCache() {
  357.   int i;
  358.  
  359.   for (i = 0; i < numFonts; ++i)
  360.     delete fonts[i];
  361. }
  362.  
  363. XOutputFont *XOutputFontCache::getFont(GfxFont *gfxFont,
  364.                        double m11, double m12,
  365.                        double m21, double m22) {
  366.   XOutputFont *font;
  367.   int i, j;
  368.  
  369.   // is it the most recently used font?
  370.   if (numFonts > 0 && fonts[0]->matches(gfxFont->getTag(),
  371.                     m11, m12, m21, m22))
  372.     return fonts[0];
  373.  
  374.   // is it in the cache?
  375.   for (i = 1; i < numFonts; ++i) {
  376.     if (fonts[i]->matches(gfxFont->getTag(), m11, m12, m21, m22)) {
  377.       font = fonts[i];
  378.       for (j = i; j > 0; --j)
  379.     fonts[j] = fonts[j-1];
  380.       fonts[0] = font;
  381.       return font;
  382.     }
  383.   }
  384.  
  385.   // make a new font
  386.   font = new XOutputFont(gfxFont, m11, m12, m21, m22, display);
  387.   if (!font->getXFont()) {
  388.     delete font;
  389.     return NULL;
  390.   }
  391.  
  392.   // insert font in cache
  393.   if (numFonts == fontCacheSize) {
  394.     --numFonts;
  395.     delete fonts[numFonts];
  396.   }
  397.   for (j = numFonts; j > 0; --j)
  398.     fonts[j] = fonts[j-1];
  399.   fonts[0] = font;
  400.   ++numFonts;
  401.  
  402.   // return it
  403.   return font;
  404. }
  405.  
  406. //------------------------------------------------------------------------
  407. // XOutputDev
  408. //------------------------------------------------------------------------
  409.  
  410. XOutputDev::XOutputDev(LTKWindow *win1) {
  411.   XGCValues gcValues;
  412.   XColor xcolor;
  413.   Colormap colormap;
  414.   int r, g, b, n;
  415.   GBool ok;
  416.  
  417.   // get pointer to X stuff
  418.   win = win1;
  419.   canvas = (LTKScrollingCanvas *)win->findWidget("canvas");
  420.   display = win->getDisplay();
  421.   screenNum = win->getScreenNum();
  422.   pixmap = canvas->getPixmap();
  423.  
  424.   // allocate a color cube
  425.   colormap = DefaultColormap(display, screenNum);
  426.   if (rgbCubeSize > maxRGBCube)
  427.     rgbCubeSize = maxRGBCube;
  428.   ok = gFalse;
  429.   for (numColors = rgbCubeSize; numColors >= 2; --numColors) {
  430.     ok = gTrue;
  431.     n = 0;
  432.     for (r = 0; r < numColors && ok; ++r) {
  433.       for (g = 0; g < numColors && ok; ++g) {
  434.     for (b = 0; b < numColors && ok; ++b) {
  435.       xcolor.red = (r * 65535) / (numColors - 1);
  436.       xcolor.green = (g * 65535) / (numColors - 1);
  437.       xcolor.blue = (b * 65535) / (numColors - 1);
  438.       if (XAllocColor(display, colormap, &xcolor))
  439.         colors[n++] = xcolor.pixel;
  440.       else
  441.         ok = gFalse;
  442.     }
  443.       }
  444.     }
  445.     if (ok)
  446.       break;
  447.     XFreeColors(display, colormap, colors, n, 0);
  448.   }
  449.   if (!ok) {
  450.     numColors = 1;
  451.     colors[0] = BlackPixel(display, screenNum);
  452.     colors[1] = WhitePixel(display, screenNum);
  453.   }
  454.  
  455.   // allocate GCs
  456.   gcValues.foreground = colors[0];
  457.   gcValues.background = WhitePixel(display, screenNum);
  458.   gcValues.line_width = 0;
  459.   gcValues.line_style = LineSolid;
  460.   strokeGC = XCreateGC(display, pixmap,
  461.                GCForeground | GCBackground | GCLineWidth | GCLineStyle,
  462.                        &gcValues);
  463.   fillGC = XCreateGC(display, pixmap,
  464.              GCForeground | GCBackground | GCLineWidth | GCLineStyle,
  465.              &gcValues);
  466.   gcValues.foreground = gcValues.background;
  467.   paperGC = XCreateGC(display, pixmap,
  468.               GCForeground | GCBackground | GCLineWidth | GCLineStyle,
  469.               &gcValues);
  470.  
  471.   // no clip region yet
  472.   clipRegion = NULL;
  473.  
  474.   // set up the font cache and fonts
  475.   gfxFont = NULL;
  476.   font = NULL;
  477.   fontCache = new XOutputFontCache(display);
  478.  
  479.   // empty state stack
  480.   save = NULL;
  481.  
  482.   // initialize graphics state
  483.   clear();
  484. }
  485.  
  486. XOutputDev::~XOutputDev() {
  487.   delete fontCache;
  488.   XFreeGC(display, strokeGC);
  489.   XFreeGC(display, fillGC);
  490.   XFreeGC(display, paperGC);
  491.   if (clipRegion)
  492.     XDestroyRegion(clipRegion);
  493. }
  494.  
  495. void XOutputDev::setPageSize(int x, int y) {
  496.   canvas->resize(x, y);
  497.   pixmap = canvas->getPixmap();
  498. }
  499.  
  500. void XOutputDev::clear() {
  501.   XOutputState *s;
  502.   XGCValues gcValues;
  503.   XRectangle rect;
  504.  
  505.   // clear state stack
  506.   while (save) {
  507.     s = save;
  508.     save = save->next;
  509.     XFreeGC(display, s->strokeGC);
  510.     XFreeGC(display, s->fillGC);
  511.     XDestroyRegion(s->clipRegion);
  512.     delete s;
  513.   }
  514.   save = NULL;
  515.  
  516.   // reset GCs
  517.   gcValues.foreground = colors[0];
  518.   gcValues.background = WhitePixel(display, screenNum);
  519.   gcValues.line_width = 0;
  520.   gcValues.line_style = LineSolid;
  521.   XChangeGC(display, strokeGC,
  522.         GCForeground | GCBackground | GCLineWidth | GCLineStyle,
  523.         &gcValues);
  524.   XChangeGC(display, fillGC,
  525.         GCForeground | GCBackground | GCLineWidth | GCLineStyle,
  526.         &gcValues);
  527.  
  528.   // clear clipping region
  529.   if (clipRegion)
  530.     XDestroyRegion(clipRegion);
  531.   clipRegion = XCreateRegion();
  532.   rect.x = rect.y = 0;
  533.   rect.width = canvas->getRealWidth();
  534.   rect.height = canvas->getRealHeight();
  535.   XUnionRectWithRegion(&rect, clipRegion, clipRegion);
  536.   XSetRegion(display, strokeGC, clipRegion);
  537.   XSetRegion(display, fillGC, clipRegion);
  538.  
  539.   // clear font
  540.   gfxFont = NULL;
  541.   font = NULL;
  542.  
  543.   // clear window
  544.   XFillRectangle(display, pixmap, paperGC,
  545.          0, 0, canvas->getRealWidth(), canvas->getRealHeight());
  546. }
  547.  
  548. void XOutputDev::dump() {
  549.   canvas->redraw();
  550. }
  551.  
  552. void XOutputDev::saveState(GfxState *state) {
  553.   XOutputState *s;
  554.   XGCValues values;
  555.  
  556.   // save current state
  557.   s = new XOutputState;
  558.   s->strokeGC = strokeGC;
  559.   s->fillGC = fillGC;
  560.   s->clipRegion = clipRegion;
  561.  
  562.   // push onto state stack
  563.   s->next = save;
  564.   save = s;
  565.  
  566.   // create a new current state by copying
  567.   strokeGC = XCreateGC(display, pixmap, 0, &values);
  568.   XCopyGC(display, s->strokeGC, 0xffffffff, strokeGC);
  569.   fillGC = XCreateGC(display, pixmap, 0, &values);
  570.   XCopyGC(display, s->fillGC, 0xffffffff, fillGC);
  571.   clipRegion = XCreateRegion();
  572.   XUnionRegion(s->clipRegion, clipRegion, clipRegion);
  573.   XSetRegion(display, strokeGC, clipRegion);
  574.   XSetRegion(display, fillGC, clipRegion);
  575. }
  576.  
  577. void XOutputDev::restoreState(GfxState *state) {
  578.   XOutputState *s;
  579.  
  580.   if (save) {
  581.     // kill current state
  582.     XFreeGC(display, strokeGC);
  583.     XFreeGC(display, fillGC);
  584.     XDestroyRegion(clipRegion);
  585.  
  586.     // restore state
  587.     strokeGC = save->strokeGC;
  588.     fillGC = save->fillGC;
  589.     clipRegion = save->clipRegion;
  590.     XSetRegion(display, strokeGC, clipRegion);
  591.     XSetRegion(display, fillGC, clipRegion);
  592.  
  593.     // pop state stack
  594.     s = save;
  595.     save = save->next;
  596.     delete s;
  597.   }
  598. }
  599.  
  600. void XOutputDev::updateAll(GfxState *state) {
  601.   updateLineAttrs(state, gTrue);
  602.   updateMiterLimit(state);
  603.   updateFillColor(state);
  604.   updateStrokeColor(state);
  605.   updateFont(state);
  606. }
  607.  
  608. void XOutputDev::updateCTM(GfxState *state) {
  609.   updateLineAttrs(state, gTrue);
  610. }
  611.  
  612. void XOutputDev::updateLineDash(GfxState *state){
  613.   updateLineAttrs(state, gTrue);
  614. }
  615.  
  616. void XOutputDev::updateLineJoin(GfxState *state) {
  617.   updateLineAttrs(state, gFalse);
  618. }
  619.  
  620. void XOutputDev::updateLineCap(GfxState *state) {
  621.   updateLineAttrs(state, gFalse);
  622. }
  623.  
  624. // unimplemented
  625. void XOutputDev::updateMiterLimit(GfxState *state) {
  626. }
  627.  
  628. void XOutputDev::updateLineWidth(GfxState *state) {
  629.   updateLineAttrs(state, gFalse);
  630. }
  631.  
  632. void XOutputDev::updateLineAttrs(GfxState *state, GBool updateDash) {
  633.   double width;
  634.   int cap, join;
  635.   double *dashPattern;
  636.   int dashLength;
  637.   double dashStart;
  638.   char dashList[20];
  639.   int i;
  640.  
  641.   width = state->getTransformedLineWidth();
  642.   switch (state->getLineCap()) {
  643.   case 0: cap = CapButt; break;
  644.   case 1: cap = CapRound; break;
  645.   case 2: cap = CapProjecting; break;
  646.   default:
  647.     error(0, "Bad line cap style (%d)", state->getLineCap());
  648.     cap = CapButt;
  649.     break;
  650.   }
  651.   switch (state->getLineJoin()) {
  652.   case 0: join = JoinMiter; break;
  653.   case 1: join = JoinRound; break;
  654.   case 2: join = JoinBevel; break;
  655.   default:
  656.     error(0, "Bad line join style (%d)", state->getLineJoin());
  657.     join = JoinMiter;
  658.     break;
  659.   }
  660.   state->getLineDash(&dashPattern, &dashLength, &dashStart);
  661.   XSetLineAttributes(display, strokeGC, xoutRound(width),
  662.              dashLength > 0 ? LineOnOffDash : LineSolid,
  663.              cap, join);
  664.   if (updateDash && dashLength > 0) {
  665.     if (dashLength > 20)
  666.       dashLength = 20;
  667.     for (i = 0; i < dashLength; ++i) {
  668.       dashList[i] = xoutRound(state->transformWidth(dashPattern[i]));
  669.       if (dashList[i] == 0)
  670.     dashList[i] = 1;
  671.     }
  672.     XSetDashes(display, strokeGC, xoutRound(dashStart), dashList, dashLength);
  673.   }
  674. }
  675.  
  676. void XOutputDev::updateFillColor(GfxState *state) {
  677.   XSetForeground(display, fillGC, findColor(state->getFillColor()));
  678. }
  679.  
  680. void XOutputDev::updateStrokeColor(GfxState *state) {
  681.   XSetForeground(display, strokeGC, findColor(state->getStrokeColor()));
  682. }
  683.  
  684. void XOutputDev::updateFont(GfxState *state) {
  685.   double m11, m12, m21, m22;
  686.  
  687.   if (!(gfxFont = state->getFont())) {
  688.     font = NULL;
  689.     return;
  690.   }
  691.   state->getFontTransMat(&m11, &m12, &m21, &m22);
  692.   font = fontCache->getFont(gfxFont, m11, m12, m21, m22);
  693.   if (font) {
  694.     XSetFont(display, fillGC, font->getXFont()->fid);
  695.     XSetFont(display, strokeGC, font->getXFont()->fid);
  696.   }
  697. }
  698.  
  699. void XOutputDev::stroke(GfxState *state) {
  700.   GfxPath *path;
  701.   GfxSubpath *subpath;
  702.   XPoint *points;
  703.   double x, y;
  704.   int n, m, i, j;
  705.  
  706.   // get path
  707.   path = state->getPath();
  708.   n = path->getNumSubpaths();
  709.  
  710.   // allocate points array
  711.   m = 0;
  712.   for (i = 0; i < n; ++i) {
  713.     subpath = path->getSubpath(i);
  714.     if (subpath->getNumPoints() > m)
  715.       m = subpath->getNumPoints();
  716.   }
  717.   if (m <= numTmpPoints)
  718.     points = tmpPoints;
  719.   else
  720.     points = (XPoint *)gmalloc(m * sizeof(XPoint));
  721.  
  722.   // draw the lines
  723.   for (i = 0; i < n; ++i) {
  724.     subpath = path->getSubpath(i);
  725.     m = subpath->getNumPoints();
  726.     for (j = 0; j < m; ++j) {
  727.       state->transform(subpath->getX(j), subpath->getY(j), &x, &y);
  728.       points[j].x = xoutRound(x);
  729.       points[j].y = xoutRound(y);
  730.     }
  731.     XDrawLines(display, pixmap, strokeGC, points, m, CoordModeOrigin);
  732.   }
  733.  
  734.   // free points
  735.   if (points != tmpPoints)
  736.     gfree(points);
  737. }
  738.  
  739. void XOutputDev::fill(GfxState *state) {
  740.   doFill(state, WindingRule);
  741. }
  742.  
  743. void XOutputDev::eoFill(GfxState *state) {
  744.   doFill(state, EvenOddRule);
  745. }
  746.  
  747. // This contains a kludge to deal with fills with multiple subpaths.
  748. // First, it divides up the subpaths into non-overlapping polygons by
  749. // simply comparing bounding rectangles.  Second it handles filling
  750. // polygons with multiple disjoint subpaths by connecting all of the
  751. // subpaths to one point.  There really ought to be a better way of
  752. // doing this.
  753. void XOutputDev::doFill(GfxState *state, int rule) {
  754.   static int tmpLengths[numTmpSubpaths];
  755.   static BoundingRect tmpRects[numTmpSubpaths];
  756.   GfxPath *path;
  757.   GfxSubpath *subpath;
  758.   XPoint *points;
  759.   int *lengths;
  760.   BoundingRect *rects;
  761.   BoundingRect rect;
  762.   double x, y;
  763.   int x1, y1;
  764.   int n, m, i, j, k;
  765.  
  766.   // set fill rule
  767.   XSetFillRule(display, fillGC, rule);
  768.  
  769.   // get path
  770.   path = state->getPath();
  771.   n = path->getNumSubpaths();
  772.  
  773.   // allocate points and lengths arrays
  774.   m = 0;
  775.   for (i = 0; i < n; ++i)
  776.     m += path->getSubpath(i)->getNumPoints() + 2;
  777.   if (m < numTmpPoints)
  778.     points = tmpPoints;
  779.   else
  780.     points = (XPoint *)gmalloc(m * sizeof(XPoint));
  781.   if (n < numTmpSubpaths)
  782.     lengths = tmpLengths;
  783.   else
  784.     lengths = (int *)gmalloc(n * sizeof(int));
  785.  
  786.   // allocate bounding rectangles array
  787.   if (n < numTmpSubpaths)
  788.     rects = tmpRects;
  789.   else
  790.     rects = (BoundingRect *)gmalloc(n * sizeof(BoundingRect));
  791.  
  792.   // transform points
  793.   k = 0;
  794.   for (i = 0; i < n; ++i) {
  795.  
  796.     // do one subpath
  797.     subpath = path->getSubpath(i);
  798.     m = subpath->getNumPoints();
  799.     for (j = 0; j < m; ++j) {
  800.  
  801.       // transform a point
  802.       state->transform(subpath->getX(j), subpath->getY(j), &x, &y);
  803.       points[k+j].x = x1 = xoutRound(x);
  804.       points[k+j].y = y1 = xoutRound(y);
  805.  
  806.       // update bounding rectangle
  807.       if (n > 1) {
  808.     if (j == 0) {
  809.       rects[i].xMin = rects[i].xMax = x1;
  810.       rects[i].yMin = rects[i].yMax = y1;
  811.     } else {
  812.       if (x1 < rects[i].xMin)
  813.         rects[i].xMin = x1;
  814.       else if (x1 > rects[i].xMax)
  815.         rects[i].xMax = x1;
  816.       if (y1 < rects[i].yMin)
  817.         rects[i].yMin = y1;
  818.       else if (y1 > rects[i].yMax)
  819.         rects[i].yMax = y1;
  820.     }
  821.       }
  822.     }
  823.  
  824.     // close subpath if necessary
  825.     if (points[k].x != points[k+m-1].x || points[k].y != points[k+m-1].y) {
  826.       points[k+m] = points[k];
  827.       ++m;
  828.     }
  829.     lengths[i] = m;
  830.  
  831.     // next subpath
  832.     k += m + 1;
  833.   }
  834.  
  835.   // only one subpath
  836.   if (n == 1) {
  837.     XFillPolygon(display, pixmap, fillGC, points, lengths[0],
  838.          Complex, CoordModeOrigin);
  839.  
  840.   // multiple subpaths
  841.   } else {
  842.     i = 0;
  843.     k = 0;
  844.     while (i < n) {
  845.       rect = rects[i];
  846.       m = lengths[i];
  847.       points[k+m] = points[k];
  848.       ++m;
  849.       for (j = i + 1; j < n; ++j) {
  850.     if (!(((rects[j].xMin > rect.xMin && rects[j].xMin < rect.xMax) ||
  851.            (rects[j].xMax > rect.xMin && rects[j].xMax < rect.xMax) ||
  852.            (rects[j].xMin < rect.xMin && rects[j].xMax > rect.xMax)) &&
  853.           ((rects[j].yMin > rect.yMin && rects[j].yMin < rect.yMax) ||
  854.            (rects[j].yMax > rect.yMin && rects[j].yMax < rect.yMax) ||
  855.            (rects[j].yMin < rect.yMin && rects[j].yMax > rect.yMax))))
  856.       break;
  857.     if (rects[j].xMin < rect.xMin)
  858.       rect.xMin = rects[j].xMin;
  859.     if (rects[j].xMax > rect.xMax)
  860.       rect.xMax = rects[j].xMax;
  861.     if (rects[j].yMin < rect.yMin)
  862.       rect.yMin = rects[j].yMin;
  863.     if (rects[j].yMax > rect.yMax)
  864.       rect.yMax = rects[j].yMax;
  865.     m += lengths[j];
  866.     points[k+m] = points[k];
  867.     ++m;
  868.       }
  869.       XFillPolygon(display, pixmap, fillGC, points + k, m,
  870.            Complex, CoordModeOrigin);
  871.       i = j;
  872.       k += m;
  873.     }
  874.   }
  875.  
  876.   // free points, lengths, and rectangles arrays
  877.   if (points != tmpPoints)
  878.     gfree(points);
  879.   if (lengths != tmpLengths)
  880.     gfree(lengths);
  881.   if (rects != tmpRects)
  882.     gfree(rects);
  883. }
  884.  
  885. void XOutputDev::clip(GfxState *state) {
  886.   XPoint *points;
  887.   int n;
  888.   Region region;
  889.  
  890.   points = pathPoints(state, &n);
  891.   region = XPolygonRegion(points, n, WindingRule);
  892.   gfree(points);
  893.   XIntersectRegion(region, clipRegion, clipRegion);
  894.   XDestroyRegion(region);
  895.   XSetRegion(display, strokeGC, clipRegion);
  896.   XSetRegion(display, fillGC, clipRegion);
  897. }
  898.  
  899. void XOutputDev::eoClip(GfxState *state) {
  900.   XPoint *points;
  901.   int n;
  902.   Region region;
  903.  
  904.   points = pathPoints(state, &n);
  905.   region = XPolygonRegion(points, n, EvenOddRule);
  906.   gfree(points);
  907.   XIntersectRegion(region, clipRegion, clipRegion);
  908.   XDestroyRegion(region);
  909.   XSetRegion(display, strokeGC, clipRegion);
  910.   XSetRegion(display, fillGC, clipRegion);
  911. }
  912.  
  913. XPoint *XOutputDev::pathPoints(GfxState *state, int *numPoints) {
  914.   XPoint *points;
  915.   GfxPath *path;
  916.   GfxSubpath *subpath;
  917.   double x, y;
  918.   int n, m, i, j, k;
  919.  
  920.   path = state->getPath();
  921.   n = path->getNumSubpaths();
  922.   m = 0;
  923.   for (i = 0; i < n; ++i)
  924.     m += path->getSubpath(i)->getNumPoints();
  925.   points = (XPoint *)gmalloc(m * sizeof(XPoint));
  926.   k = 0;
  927.   for (i = 0; i < n; ++i) {
  928.     subpath = path->getSubpath(i);
  929.     for (j = 0; j < subpath->getNumPoints(); ++j) {
  930.       state->transform(subpath->getX(j), subpath->getY(j), &x, &y);
  931.       points[k].x = xoutRound(x);
  932.       points[k].y = xoutRound(y);
  933.       ++k;
  934.     }
  935.   }
  936.   *numPoints = m;
  937.   return points;
  938. }
  939.  
  940. void XOutputDev::drawChar(GfxState *state, double x, double y, Guchar c) {
  941.   Gushort c1;
  942.   char buf;
  943.   char *p;
  944.   int n, i;
  945.   double x1, y1;
  946.   double tx;
  947.  
  948.   if (!font)
  949.     return;
  950.   state->transform(x, y, &x1, &y1);
  951.   c1 = font->mapChar(c);
  952.   if (c1 <= lastRegularChar) {
  953.     buf = (char)c1;
  954.     XDrawString(display, pixmap,
  955.         (state->getRender() & 1) ? strokeGC : fillGC,
  956.         xoutRound(x1), xoutRound(y1), &buf, 1);
  957.   } else if (c1 <= lastSubstChar) {
  958.     buf = (char)substChars[c1 - firstSubstChar];
  959.     XDrawString(display, pixmap,
  960.         (state->getRender() & 1) ? strokeGC : fillGC,
  961.         xoutRound(x1), xoutRound(y1), &buf, 1);
  962.   } else if (c1 <= lastConstrChar) {
  963.     //~ need to deal with rotated text here
  964.     switch (c1 - firstConstrChar) {
  965.     case 0: // bullet
  966.       tx = 0.25 * state->getTransformedFontSize() * 
  967.            gfxFont->getWidth(c);
  968.       XFillRectangle(display, pixmap,
  969.              (state->getRender() & 1) ? strokeGC : fillGC,
  970.              xoutRound(x1 + tx),
  971.              xoutRound(y1 - 0.4 * font->getXFont()->ascent - tx),
  972.              xoutRound(2 * tx), xoutRound(2 * tx));
  973.       break;
  974.     case 1: // trademark
  975. //~      tx = state->getTransformedFontSize() *
  976. //~           (gfxFont->getWidth(c) -
  977. //~            gfxFont->getWidth(font->revCharMap('M')));
  978.       tx = 0.9 * state->getTransformedFontSize() *
  979.            gfxFont->getWidth(font->revMapChar('T'));
  980.       y1 -= 0.33 * (double)font->getXFont()->ascent;
  981.       buf = 'T';
  982.       XDrawString(display, pixmap,
  983.           (state->getRender() & 1) ? strokeGC : fillGC,
  984.           xoutRound(x1), xoutRound(y1), &buf, 1);
  985.       x1 += tx;
  986.       buf = 'M';
  987.       XDrawString(display, pixmap,
  988.           (state->getRender() & 1) ? strokeGC : fillGC,
  989.           xoutRound(x1), xoutRound(y1), &buf, 1);
  990.       break;
  991.     }
  992.   } else if (c1 <= lastMultiChar) {
  993.     p = multiChars[c1 - firstMultiChar];
  994.     n = strlen(p);
  995.     tx = gfxFont->getWidth(c);
  996.     for (i = 1; i < n; ++i)
  997.       tx -= gfxFont->getWidth(font->revMapChar(p[i]));
  998.     tx = tx * state->getTransformedFontSize() / (double)(n - 1);
  999.     for (i = 0; i < n; ++i) {
  1000.       XDrawString(display, pixmap,
  1001.           (state->getRender() & 1) ? strokeGC : fillGC,
  1002.           xoutRound(x1), xoutRound(y1), p + i, 1);
  1003.       x1 += tx;
  1004.     }
  1005.   }
  1006. }
  1007.  
  1008. void XOutputDev::drawImageMask(GfxState *state, Stream *str,
  1009.                    int width, int height, GBool invert) {
  1010.   XImage *image;
  1011.   int x0, y0;            // top left corner of image
  1012.   int w0, h0, w1, h1;        // size of image
  1013.   Guint depth;
  1014.   double xt, yt, wt, ht;
  1015.   GBool rotate, xFlip, yFlip;
  1016.   int x, y;
  1017.   int ix, iy;
  1018.   int px1, px2, qx, dx;
  1019.   int py1, py2, qy, dy;
  1020.   Guchar *pixLine;
  1021.   Gulong color;
  1022.   Gulong buf;
  1023.   int i, j;
  1024.  
  1025.   // get image position and size
  1026.   state->transform(0, 0, &xt, &yt);
  1027.   state->transformDelta(1, 1, &wt, &ht);
  1028.   if (wt > 0) {
  1029.     x0 = xoutRound(xt);
  1030.     w0 = xoutRound(wt);
  1031.   } else {
  1032.     x0 = xoutRound(xt + wt);
  1033.     w0 = xoutRound(-wt);
  1034.   }
  1035.   if (ht > 0) {
  1036.     y0 = xoutRound(yt);
  1037.     h0 = xoutRound(ht);
  1038.   } else {
  1039.     y0 = xoutRound(yt + ht);
  1040.     h0 = xoutRound(-ht);
  1041.   }
  1042.   state->transformDelta(1, 0, &xt, &yt);
  1043.   rotate = fabs(xt) < fabs(yt);
  1044.   if (rotate) {
  1045.     w1 = h0;
  1046.     h1 = w0;
  1047.     xFlip = ht < 0;
  1048.     yFlip = wt > 0;
  1049.   } else {
  1050.     w1 = w0;
  1051.     h1 = h0;
  1052.     xFlip = wt < 0;
  1053.     yFlip = ht > 0;
  1054.   }
  1055.  
  1056.   // set up
  1057.   str->reset();
  1058.   color = findColor(state->getFillColor());
  1059.  
  1060.   // check for tiny (zero width or height) images
  1061.   if (w0 == 0 || h0 == 0) {
  1062.     j = height * ((width + 7) / 8);
  1063.     for (i = 0; i < j; ++i)
  1064.       str->getChar();
  1065.     return;
  1066.   }
  1067.  
  1068.   // Bresenham parameters
  1069.   px1 = w1 / width;
  1070.   px2 = w1 - px1 * width;
  1071.   py1 = h1 / height;
  1072.   py2 = h1 - py1 * height;
  1073.  
  1074.   // allocate XImage
  1075.   depth = DefaultDepth(display, screenNum);
  1076.   if (x0 < 0 || x0 + w0 > canvas->getRealWidth() ||
  1077.       y0 < 0 || y0 + h0 > canvas->getRealHeight()) {
  1078.     error(0, "Badly placed image mask");
  1079.     return;
  1080.   }
  1081.   image = XGetImage(display, pixmap, x0, y0, w0, h0,
  1082.             (1 << depth) - 1, ZPixmap);
  1083.  
  1084.   // allocate line buffer
  1085.   pixLine = (Guchar *)gmalloc(((width + 7) & ~7) * sizeof(Guchar));
  1086.  
  1087.   // first line (column)
  1088.   y = yFlip ? h1 - 1 : 0;
  1089.   qy = 0;
  1090.  
  1091.   // read image
  1092.   for (i = 0; i < height; ++i) {
  1093.  
  1094.     // vertical (horizontal) Bresenham
  1095.     dy = py1;
  1096.     if ((qy += py2) >= height) {
  1097.       ++dy;
  1098.       qy -= height;
  1099.     }
  1100.  
  1101.     // first column (line)
  1102.     x = xFlip ? w1 - 1 : 0;
  1103.     qx = 0;
  1104.  
  1105.     // read a line (column)
  1106.     for (j = 0; j < width; j += 8) {
  1107.       buf = str->getChar();
  1108.       if (invert)
  1109.     buf = buf ^ 0xff;
  1110.       pixLine[j+7] = buf & 1;
  1111.       buf >>= 1;
  1112.       pixLine[j+6] = buf & 1;
  1113.       buf >>= 1;
  1114.       pixLine[j+5] = buf & 1;
  1115.       buf >>= 1;
  1116.       pixLine[j+4] = buf & 1;
  1117.       buf >>= 1;
  1118.       pixLine[j+3] = buf & 1;
  1119.       buf >>= 1;
  1120.       pixLine[j+2] = buf & 1;
  1121.       buf >>= 1;
  1122.       pixLine[j+1] = buf & 1;
  1123.       buf >>= 1;
  1124.       pixLine[j] = buf & 1;
  1125.     }
  1126.  
  1127.     // draw line (column) in XImage
  1128.     if (dy > 0) {
  1129.  
  1130.       // for each column (line)...
  1131.       for (j = 0; j < width; ++j) {
  1132.  
  1133.     // horizontal (vertical) Bresenham
  1134.     dx = px1;
  1135.     if ((qx += px2) >= width) {
  1136.       ++dx;
  1137.       qx -= width;
  1138.     }
  1139.  
  1140.     // draw image pixel
  1141.     if (dx > 0 && pixLine[j] == 0) {
  1142.       if (dx == 1 && dy == 1) {
  1143.         if (rotate)
  1144.           XPutPixel(image, y, x, color);
  1145.         else
  1146.           XPutPixel(image, x, y, color);
  1147.       } else {
  1148.         for (ix = 0; ix < dx; ++ix) {
  1149.           for (iy = 0; iy < dy; ++iy) {
  1150.         if (rotate)
  1151.           XPutPixel(image, yFlip ? y - iy : y + iy,
  1152.                 xFlip ? x - ix : x + ix, color);
  1153.         else
  1154.           XPutPixel(image, xFlip ? x - ix : x + ix,
  1155.                 yFlip ? y - iy : y + iy, color);
  1156.           }
  1157.         }
  1158.       }
  1159.     }
  1160.  
  1161.     // next column (line)
  1162.     if (xFlip)
  1163.       x -= dx;
  1164.     else
  1165.       x += dx;
  1166.       }
  1167.     }
  1168.  
  1169.     // next line (column)
  1170.     if (yFlip)
  1171.       y -= dy;
  1172.     else
  1173.       y += dy;
  1174.   }
  1175.  
  1176.   // blit the image into the pixmap
  1177.   XPutImage(display, pixmap, fillGC, image, 0, 0, x0, y0, w0, h0);
  1178.  
  1179.   // free memory
  1180.   XDestroyImage(image);
  1181.   gfree(pixLine);
  1182. }
  1183.  
  1184. inline Gulong XOutputDev::findColor(RGBColor *x, RGBColor *err) {
  1185.   int r, g, b;
  1186.   double gray;
  1187.   Gulong pixel;
  1188.  
  1189.   if (numColors == 1) {
  1190.     gray = 0.299 * x->r + 0.587 * x->g + 0.114 * x->b;
  1191.     if (gray < 0.5) {
  1192.       pixel = colors[0];
  1193.       err->r = x->r;
  1194.       err->g = x->g;
  1195.       err->b = x->b;
  1196.     } else {
  1197.       pixel = colors[1];
  1198.       err->r = x->r - 255;
  1199.       err->g = x->g - 255;
  1200.       err->b = x->b - 255;
  1201.     }
  1202.   } else {
  1203.     r = (x->r * (numColors - 1) + 128) >> 8;
  1204.     g = (x->g * (numColors - 1) + 128) >> 8;
  1205.     b = (x->b * (numColors - 1) + 128) >> 8;
  1206.     pixel = colors[(r * numColors + g) * numColors + b];
  1207.     err->r = x->r - ((r << 8) - r) / (numColors - 1);
  1208.     err->g = x->g - ((g << 8) - g) / (numColors - 1); 
  1209.     err->b = x->b - ((b << 8) - b) / (numColors - 1);
  1210.   }
  1211.   return pixel;
  1212. }
  1213.  
  1214. void XOutputDev::drawImage(GfxState *state, Stream *str, int width,
  1215.                int height, GfxColorSpace *colorSpace) {
  1216.   XImage *image;
  1217.   int x0, y0;            // top left corner of image
  1218.   int w0, h0, w1, h1;        // size of image
  1219.   Guint depth;
  1220.   double xt, yt, wt, ht;
  1221.   GBool rotate, xFlip, yFlip;
  1222.   GBool dither;
  1223.   int x, y;
  1224.   int ix, iy;
  1225.   int px1, px2, qx, dx;
  1226.   int py1, py2, qy, dy;
  1227.   Guchar *pixLine;
  1228.   Gulong pixel;
  1229.   Gulong buf, bitMask;
  1230.   int bits;
  1231.   int nComps, nVals, nBits;
  1232.   Guchar r1, g1, b1;
  1233.   RGBColor color2, err;
  1234.   RGBColor *errRight, *errDown;
  1235.   int i, j, k;
  1236.  
  1237.   // get image position and size
  1238.   state->transform(0, 0, &xt, &yt);
  1239.   state->transformDelta(1, 1, &wt, &ht);
  1240.   if (wt > 0) {
  1241.     x0 = xoutRound(xt);
  1242.     w0 = xoutRound(wt);
  1243.   } else {
  1244.     x0 = xoutRound(xt + wt);
  1245.     w0 = xoutRound(-wt);
  1246.   }
  1247.   if (ht > 0) {
  1248.     y0 = xoutRound(yt);
  1249.     h0 = xoutRound(ht);
  1250.   } else {
  1251.     y0 = xoutRound(yt + ht);
  1252.     h0 = xoutRound(-ht);
  1253.   }
  1254.   state->transformDelta(1, 0, &xt, &yt);
  1255.   rotate = fabs(xt) < fabs(yt);
  1256.   if (rotate) {
  1257.     w1 = h0;
  1258.     h1 = w0;
  1259.     xFlip = ht < 0;
  1260.     yFlip = wt > 0;
  1261.   } else {
  1262.     w1 = w0;
  1263.     h1 = h0;
  1264.     xFlip = wt < 0;
  1265.     yFlip = ht > 0;
  1266.   }
  1267.  
  1268.   // set up
  1269.   str->reset();
  1270.   nComps = colorSpace->getNumComponents();
  1271.   nVals = width * nComps;
  1272.   nBits = colorSpace->getBits();
  1273.   dither = nComps > 1 || nBits > 1;
  1274.  
  1275.   // check for tiny (zero width or height) images
  1276.   if (w0 == 0 || h0 == 0) {
  1277.     k = height * ((nVals * nBits + 7) / 8);
  1278.     for (i = 0; i < k; ++i)
  1279.       str->getChar();
  1280.     return;
  1281.   }
  1282.  
  1283.   // Bresenham parameters
  1284.   px1 = w1 / width;
  1285.   px2 = w1 - px1 * width;
  1286.   py1 = h1 / height;
  1287.   py2 = h1 - py1 * height;
  1288.  
  1289.   // allocate XImage
  1290.   depth = DefaultDepth(display, screenNum);
  1291.   image = XCreateImage(display, DefaultVisual(display, screenNum),
  1292.                depth, ZPixmap, 0, NULL, w0, h0, 8, 0);
  1293.   image->data = (char *)gmalloc(h0 * image->bytes_per_line);
  1294.  
  1295.   // allocate line buffer
  1296.   pixLine = (Guchar *)gmalloc(((nVals + 7) & ~7) * sizeof(Guchar));
  1297.  
  1298.   // allocate error diffusion accumulators
  1299.   if (dither) {
  1300.     errDown = (RGBColor *)gmalloc(w1 * sizeof(RGBColor));
  1301.     errRight = (RGBColor *)gmalloc((py1 + 1) * sizeof(RGBColor));
  1302.     for (j = 0; j < w1; ++j)
  1303.       errDown[j].r = errDown[j].g = errDown[j].b = 0;
  1304.   } else {
  1305.     errDown = NULL;
  1306.     errRight = NULL;
  1307.   }
  1308.  
  1309.   // first line (column)
  1310.   y = yFlip ? h1 - 1 : 0;
  1311.   qy = 0;
  1312.  
  1313.   // read image
  1314.   for (i = 0; i < height; ++i) {
  1315.  
  1316.     // vertical (horizontal) Bresenham
  1317.     dy = py1;
  1318.     if ((qy += py2) >= height) {
  1319.       ++dy;
  1320.       qy -= height;
  1321.     }
  1322.  
  1323.     // first column (line)
  1324.     x = xFlip ? w1 - 1 : 0;
  1325.     qx = 0;
  1326.  
  1327.     // read a line (column)
  1328.     if (nBits == 1) {
  1329.       for (j = 0; j < nVals; j += 8) {
  1330.     buf = str->getChar();
  1331.     pixLine[j+7] = buf & 1;
  1332.     buf >>= 1;
  1333.     pixLine[j+6] = buf & 1;
  1334.     buf >>= 1;
  1335.     pixLine[j+5] = buf & 1;
  1336.     buf >>= 1;
  1337.     pixLine[j+4] = buf & 1;
  1338.     buf >>= 1;
  1339.     pixLine[j+3] = buf & 1;
  1340.     buf >>= 1;
  1341.     pixLine[j+2] = buf & 1;
  1342.     buf >>= 1;
  1343.     pixLine[j+1] = buf & 1;
  1344.     buf >>= 1;
  1345.     pixLine[j] = buf & 1;
  1346.       }
  1347.     } else if (nBits == 8) {
  1348.       for (j = 0; j < nVals; ++j)
  1349.     pixLine[j] = str->getChar();
  1350.     } else {
  1351.       bitMask = (1 << nBits) - 1;
  1352.       buf = 0;
  1353.       bits = 0;
  1354.       for (j = 0; j < nVals; ++j) {
  1355.     if (bits < nBits) {
  1356.       buf = (buf << 8) | (str->getChar() & 0xff);
  1357.       bits += 8;
  1358.     }
  1359.     pixLine[j] = (buf >> (bits - nBits)) & bitMask;
  1360.     bits -= nBits;
  1361.       }
  1362.     }
  1363.  
  1364.     // draw line (column) in XImage
  1365.     if (dy > 0) {
  1366.  
  1367.       // clear error accumulator
  1368.       if (dither) {
  1369.     for (j = 0; j <= py1; ++j)
  1370.       errRight[j].r = errRight[j].g = errRight[j].b = 0;
  1371.       }
  1372.  
  1373.       // for each column (line)...
  1374.       for (j = 0, k = 0; j < width; ++j, k += nComps) {
  1375.  
  1376.     // horizontal (vertical) Bresenham
  1377.     dx = px1;
  1378.     if ((qx += px2) >= width) {
  1379.       ++dx;
  1380.       qx -= width;
  1381.     }
  1382.  
  1383.     // draw image pixel
  1384.     if (dx > 0) {
  1385.       colorSpace->getRGB(&pixLine[k], &r1, &g1, &b1);
  1386.       if (dither) {
  1387.         pixel = 0;
  1388.       } else {
  1389.         color2.r = r1;
  1390.         color2.g = g1;
  1391.         color2.b = b1;
  1392.         pixel = findColor(&color2, &err);
  1393.       }
  1394.       if (dx == 1 && dy == 1) {
  1395.         if (dither) {
  1396.           color2.r = r1 + errRight[0].r + errDown[x].r;
  1397.           if (color2.r > 255)
  1398.         color2.r = 255;
  1399.           color2.g = g1 + errRight[0].g + errDown[x].g;
  1400.           if (color2.g > 255)
  1401.         color2.g = 255;
  1402.           color2.b = b1 + errRight[0].b + errDown[x].b;
  1403.           if (color2.b > 255)
  1404.         color2.b = 255;
  1405.           pixel = findColor(&color2, &err);
  1406.           errRight[0].r = err.r / 2;
  1407.           errRight[0].g = err.g / 2;
  1408.           errRight[0].b = err.b / 2;
  1409.           errDown[x].r = err.r - errRight[0].r;
  1410.           errDown[x].g = err.g - errRight[0].g;
  1411.           errDown[x].b = err.b - errRight[0].b;
  1412.         }
  1413.         if (rotate)
  1414.           XPutPixel(image, y, x, pixel);
  1415.         else
  1416.           XPutPixel(image, x, y, pixel);
  1417.       } else {
  1418.         for (iy = 0; iy < dy; ++iy) {
  1419.           for (ix = 0; ix < dx; ++ix) {
  1420.         if (dither) {
  1421.           color2.r = r1 + errRight[iy].r +
  1422.             errDown[xFlip ? x - ix : x + ix].r;
  1423.           if (color2.r > 255)
  1424.             color2.r = 255;
  1425.           color2.g = g1 + errRight[iy].g +
  1426.             errDown[xFlip ? x - ix : x + ix].g;
  1427.           if (color2.g > 255)
  1428.             color2.g = 255;
  1429.           color2.b = b1 + errRight[iy].b +
  1430.             errDown[xFlip ? x - ix : x + ix].b;
  1431.           if (color2.b > 255)
  1432.             color2.b = 255;
  1433.           pixel = findColor(&color2, &err);
  1434.           errRight[iy].r = err.r / 2;
  1435.           errRight[iy].g = err.g / 2;
  1436.           errRight[iy].b = err.b / 2;
  1437.           errDown[xFlip ? x - ix : x + ix].r = err.r - errRight[iy].r;
  1438.           errDown[xFlip ? x - ix : x + ix].g = err.g - errRight[iy].g;
  1439.           errDown[xFlip ? x - ix : x + ix].b = err.b - errRight[iy].b;
  1440.         }
  1441.         if (rotate)
  1442.           XPutPixel(image, yFlip ? y - iy : y + iy,
  1443.                 xFlip ? x - ix : x + ix, pixel);
  1444.         else
  1445.           XPutPixel(image, xFlip ? x - ix : x + ix,
  1446.                 yFlip ? y - iy : y + iy, pixel);
  1447.           }
  1448.         }
  1449.       }
  1450.     }
  1451.  
  1452.     // next column (line)
  1453.     if (xFlip)
  1454.       x -= dx;
  1455.     else
  1456.       x += dx;
  1457.       }
  1458.     }
  1459.  
  1460.     // next line (column)
  1461.     if (yFlip)
  1462.       y -= dy;
  1463.     else
  1464.       y += dy;
  1465.   }
  1466.  
  1467.   // blit the image into the pixmap
  1468.   XPutImage(display, pixmap, fillGC, image, 0, 0, x0, y0, w0, h0);
  1469.  
  1470.   // free memory
  1471.   gfree(image->data);
  1472.   image->data = NULL;
  1473.   XDestroyImage(image);
  1474.   gfree(pixLine);
  1475.   gfree(errRight);
  1476.   gfree(errDown);
  1477. }
  1478.  
  1479. Gulong XOutputDev::findColor(GfxColor *color) {
  1480.   int r, g, b;
  1481.   double gray;
  1482.   Gulong pixel;
  1483.  
  1484.   if (numColors == 1) {
  1485.     gray = color->getGray();
  1486.     if (gray < 0.5)
  1487.       pixel = colors[0];
  1488.     else
  1489.       pixel = colors[1];
  1490.   } else {
  1491.     r = xoutRound(color->getR() * (numColors - 1));
  1492.     g = xoutRound(color->getG() * (numColors - 1));
  1493.     b = xoutRound(color->getB() * (numColors - 1));
  1494.     pixel = colors[(r * numColors + g) * numColors + b];
  1495.   }
  1496.   return pixel;
  1497. }
  1498.